package com.csp.util;

import cn.hutool.aop.ProxyUtil;
import com.csp.TestInterface;
import com.csp.anotation.Delete;
import com.csp.anotation.Insert;
import com.csp.anotation.Select;
import com.csp.anotation.Update;
import com.csp.anotation.mng.AnnotationCache;
import com.csp.anotation.mng.MethodMapping;
import com.csp.session.ExecuteMsg;
import com.csp.session.SqlSession;
import com.csp.session.SqlSessionFactoryInstance;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

/**
 * mapper接口代理
 *
 * @author:chengsp
 * @createTime:2019/8/23 11:14
 */
public class MapperProxyUtil {
    /**
     * 代理指定的mapper接口
     * @param tClass 接口class
     * @param <T> 接口类型
     * @return
     */
    public static  <T> T proxy(Class<T> tClass) {
        return ProxyUtil.newProxyInstance(new MyInvocationHandler(), tClass);
    }

    private static class MyInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionFactoryInstance.getInstance().create();
            Class<?> returnType = method.getReturnType();
            MethodMapping methodMapping = AnnotationCache.getMethodMapping(method);
            methodMapping.setMethod(method);
            methodMapping.setReturnClass(returnType);
            Annotation annotation = getAnnotation(method);
            if (annotation == null) {
                return null;
            }
            methodMapping.setAnnotation(annotation);
            Object result = null;
            sqlSession.startTransaction();
            if (annotation instanceof Select) {
                String sql = ((Select) annotation).value();
                methodMapping.setOriginalSql(sql);
                ExecuteMsg executeMsg = new ExecuteMsg(args, methodMapping);
                if (returnType.equals(List.class)) {
                    result = sqlSession.selectList(methodMapping.getPreparedSql(), executeMsg);
                } else if (returnType.equals(Set.class)) {
                    result = sqlSession.selectSet(methodMapping.getPreparedSql(), executeMsg);
                }
            } else if (annotation instanceof Update) {
                String sql = ((Update) annotation).value();
                methodMapping.setOriginalSql(sql);
                ExecuteMsg executeMsg = new ExecuteMsg(args, methodMapping);
                result = sqlSession.update(methodMapping.getPreparedSql(), executeMsg);
            } else if (annotation instanceof Delete) {
                String sql = ((Delete) annotation).value();
                methodMapping.setOriginalSql(sql);
                ExecuteMsg executeMsg = new ExecuteMsg(args, methodMapping);
                result = sqlSession.delete(methodMapping.getPreparedSql(), executeMsg);
            } else if (annotation instanceof Insert) {
                String sql = ((Insert) annotation).value();
                methodMapping.setOriginalSql(sql);
                ExecuteMsg executeMsg = new ExecuteMsg(args, methodMapping);
                result = sqlSession.insert(methodMapping.getPreparedSql(), executeMsg);
            }

            sqlSession.commit();
            sqlSession.close();
            return result;
        }

        private Annotation getAnnotation(Method method) {
            Annotation anno = AnnotationCache.getAnnotation(method);
            if (anno == null) {
                Annotation[] annotations = method.getDeclaredAnnotations();
                for (Annotation annotation : annotations) {
                    if (annotation instanceof Select || annotation instanceof Update ||
                            annotation instanceof Delete || annotation instanceof Insert) {
                        AnnotationCache.putMethodAnnotation(method, annotation);
                        return annotation;
                    }
                }
            }
            return anno;
        }
    }
}
